__checkReturn bool Native_GetPEInfo(
		__in    char *pMod,
		__inout IMAGE_DOS_HEADER *Dos,
		__inout IMAGE_NT_HEADERS *Nt,
		__inout IMAGE_OPTIONAL_HEADER *outpeXH,
		__inout IMAGE_SECTION_HEADER **outSecHdr
		)
{
	// read MZ Header
	IMAGE_DOS_HEADER *mzH;
	mzH = (IMAGE_DOS_HEADER *)pMod;

	if(mzH->e_magic != 0x5a4d)		// MZ
	{
		//printf("File does not have MZ header\n");
		return false;
	}

	// read PE Header
	IMAGE_NT_HEADERS *peH;
	peH = (IMAGE_NT_HEADERS *)(pMod + mzH->e_lfanew);

	if(peH->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER))
	{
		//printf("Unexpected option header size.\n");
		
		return false;
	}

	// read PE Ext Header
	IMAGE_OPTIONAL_HEADER *peXH;
	peXH = (IMAGE_OPTIONAL_HEADER *)((char *)peH + sizeof(IMAGE_NT_HEADERS));

	// read the sections
	IMAGE_SECTION_HEADER *secHdr = (IMAGE_SECTION_HEADER *)((char *)peXH + sizeof(IMAGE_OPTIONAL_HEADER));

	*Dos = *mzH;
	*Nt = *peH;
	*outpeXH = *peXH;
	*outSecHdr = secHdr;

	return true;
}


//*******************************************************************************************************
// Returns the total size required to load a PE image into memory
//
//*******************************************************************************************************

unsigned int Native_GetImageSize(
		__in IMAGE_DOS_HEADER *inMZ,
		__in IMAGE_NT_HEADERS *inPE,
		__in IMAGE_OPTIONAL_HEADER *inpeXH,
		__in IMAGE_SECTION_HEADER *inSecHdr
		)
{
	unsigned int result = 0;
	unsigned int alignment = inpeXH->SectionAlignment;

	if(inpeXH->SizeOfHeaders * alignment == 0)
		result += inpeXH->SizeOfHeaders;
	else
	{
		unsigned int val = inpeXH->SizeOfHeaders / alignment;
		val++;
		result += (val * alignment);
	}
	for(int i = 0; i < inPE->FileHeader.NumberOfSections; i++)
	{
		if(inSecHdr[i].Misc.VirtualSize)
		{
			if(inSecHdr[i].Misc.VirtualSize % alignment == 0)
				result += inSecHdr[i].Misc.VirtualSize;
			else
			{
				unsigned int val = inSecHdr[i].Misc.VirtualSize / alignment;
				val++;
				result += (val * alignment);
			}
		}
	}

	return result;
}


//*******************************************************************************************************
// Returns the aligned size of a section
//
//*******************************************************************************************************

unsigned long Native_GetAlignedSize(
		__in unsigned long curSize,
		__in unsigned long alignment
		)
{
	if(curSize && alignment)
	{
		if(curSize % alignment == 0)
			return curSize;
		else
		{
			int val = curSize / alignment;
			val++;
			return (val * alignment);
		}
	}
	return curSize;
}

//*******************************************************************************************************
// Copy a PE image from exePtr to ptrLoc with proper memory alignment of all sections
//
//*******************************************************************************************************

__checkReturn bool Native_LoadShare(
		__in char *exePtr,//BYTE* to The Base Address of a Mapped Image ..
		__in IMAGE_DOS_HEADER *inMZ,//Optional..
		__in IMAGE_NT_HEADERS *inPE,
		__in IMAGE_OPTIONAL_HEADER *inpeXH,
		__in IMAGE_SECTION_HEADER *inSecHdr,//Optional
		__in LPVOID ptrLoc//pointer to the location we want to write to..
		)
{
	__try
	{
		char *outPtr = (char *)ptrLoc;
		memcpy(outPtr, exePtr, inpeXH->SizeOfHeaders);
		outPtr += Native_GetAlignedSize(inpeXH->SizeOfHeaders, inpeXH->SectionAlignment);
		for(int i = 0; i < inPE->FileHeader.NumberOfSections; i++)
		{
			if(inSecHdr[i].SizeOfRawData > 0)
			{
				unsigned long toRead = inSecHdr[i].SizeOfRawData;
				if(toRead > inSecHdr[i].Misc.VirtualSize)
				{
					toRead = inSecHdr[i].Misc.VirtualSize;
					memcpy(outPtr, exePtr + inSecHdr[i].PointerToRawData, toRead);
					outPtr += Native_GetAlignedSize(inSecHdr[i].Misc.VirtualSize, inpeXH->SectionAlignment);
				}	
			}
		}
		return true;
	}
	__except(1)
	{
		return false;
	}
}
//szPathToModule = NT Path to module 
//Pid targets the process we want to map to :d
//MapSelf true = mapfile to self, Pid is assumed to be 0
//and we check what LoadCheckAlignment is,
//if it is FALSE then we default to checking csrss for imagebase.
//if LoadCheckAlignment is true we do the mentioned process below 
//and load the module into memory before via LdrLoadDll and then map it to self.
//MapSelf false = map file to process specified by Pid
//LoadCheckAlignment if true LdrLoadLibrary is called with szPathToModule
//as a parameter and upon success query the modules memory alignment.

HANDLE Native_MapModuleQueryAlignment(__in wchar_t *szPathToModule,__in_opt HANDLE Pid,__in BOOLEAN MapSelf,BOOLEAN LoadCheckAlignment)
{
	HANDLE hSection = 0;
	HANDLE hFile = 0;
	HANDLE TargetProcess = 0;
	ULONG TargetRegion = 0;
	ULONG Index = 0;
	PVOID TargetModule = 0;
	FILE_STANDARD_INFORMATION FileInformation = {0};
	LARGE_INTEGER FileSize = {0};
	OBJECT_ATTRIBUTES oa;
	DEBUG_BUFFER *pDbgbuf = {0};
	IO_STATUS_BLOCK StatusBlock = {0};
	UNICODE_STRING Unicode = {0};
	CLIENT_ID  ClientId = {0};
	char mbPath[255] = {0};
	wchar_t Err[255] = {0};
	SIZE_T ViewSize = 0;
	NTSTATUS Status = 0;
	BOOLEAN Enabled = 0;
	int Pathtest = 1;
	GETCSRPROCESSID GetCsrPid = 0;
	
	__try
	{
		Status = RtlAdjustPrivilege( 20L ,true,false,&Enabled);
		if(!NT_SUCCESS(Status))
		{
			RtlInitUnicodeString(&Unicode,L"RtlAdjustPrivilege Status:");
			*(PULONG)0 = 0;
		}
		if(MapSelf == TRUE)//use csrss 
		{
			GetCsrPid =(GETCSRPROCESSID)Native_GetApi(L"ntdll.dll","CsrGetProcessId");
			Pid = GetCsrPid();
			TargetProcess = NtCurrentProcess();
			if(Pid == 0)
			{
				Status = 0xc0000005;
				RtlInitUnicodeString(&Unicode,L"CsrGetProcessIdEx returned 0");
				*(PULONG)0 = 0;
			}
		}
		else//open the process
		{
			ClientId.UniqueProcess = Pid;
			ClientId.UniqueThread = 0;
			Status = NtOpenProcess(&TargetProcess,PROCESS_ALL_ACCESS,0,&ClientId);
			if(!NT_SUCCESS(Status))
			{
				RtlInitUnicodeString(&Unicode,L"NtOpenProcess Status");
				*(PULONG)0 = 0;
			}
			LoadCheckAlignment = TRUE;
		}
		if(LoadCheckAlignment == FALSE)
		{
			RtlInitUnicodeString(&Unicode,szPathToModule);
	 		pDbgbuf = RtlCreateQueryDebugBuffer(0,0);
			Status = RtlQueryProcessDebugInformation((ULONG)Pid,PDI_MODULES,pDbgbuf);
			if(!NT_SUCCESS(Status))
			{
				RtlInitUnicodeString(&Unicode,L"RtlQueryProcessDebugInformation Status:");
				*(PULONG)0 = 0;
			}
			wcscpy((wchar_t*)&Err,Unicode.Buffer);
			Index = 0;
			do
			{
				Index++;
				Pathtest = wcsncmp(&Err[Index],L"C",1);
			}while(Pathtest != 0);
			wcstombs((char*)&mbPath,(wchar_t*)&Err[Index],wcslen((wchar_t*)&Err[Index]));
			for(Index = 0;Index<=pDbgbuf->ModuleInformation->Count;Index++)
			{			
				Pathtest = strcmp((char*)pDbgbuf->ModuleInformation->DbgModInfo[Index].ImageName,(char*)&mbPath);
				if(Pathtest == 0)
				{
					TargetRegion = pDbgbuf->ModuleInformation->DbgModInfo[Index].Base;
					break;
				}
			}
			Status = RtlDestroyQueryDebugBuffer(pDbgbuf);
			if(!NT_SUCCESS(Status))
			{
				RtlInitUnicodeString(&Unicode,L"RtlDestroyQueryDebugBuffer Status:");
				*(PULONG)0 = 0;
			}
			RtlAdjustPrivilege(20L,false,false,&Enabled);
		}
		if(LoadCheckAlignment == TRUE)
		{
			RtlInitUnicodeString(&Unicode,szPathToModule);
			wcscpy((wchar_t*)&Err,Unicode.Buffer);
			do
			{
				if(wcsncmp(&Err[Pathtest],L"C",1) == 0)
				{
					break;
				}
				Pathtest++;
			}while(Pathtest <= Unicode.MaximumLength);
			Pathtest = (int)wcslen((wchar_t*)&Err);
			do
			{
				if(wcsncmp(&Err[Pathtest],L"\\",1) == 0)
				{
					break;
				}
				Pathtest--;
			}while(Pathtest != 0);
			RtlInitUnicodeString(&Unicode,&Err[Pathtest+1]);
			Status = LdrLoadDll(0,0,&Unicode,&TargetModule);
			if(!NT_SUCCESS(Status))
			{
				RtlInitUnicodeString(&Unicode,L"LdrLoadDll Status:");
				*(PULONG)0 = 0;
			}
			TargetRegion = (ULONG)TargetModule;
			LdrUnloadDll((HANDLE)TargetModule);
		}
		RtlInitUnicodeString(&Unicode,szPathToModule);
		InitializeObjectAttributes(&oa,&Unicode,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,0,0);
		Status = NtCreateFile(&hFile,GENERIC_READ | GENERIC_EXECUTE,&oa,&StatusBlock,0,FILE_ATTRIBUTE_NORMAL,0,FILE_OPEN,0,0,0);
		if(!NT_SUCCESS(Status))
		{
			RtlInitUnicodeString(&Unicode,L"NtCreateFile Status:");
			*(PULONG)0 = 0;
		}
		Status = NtQueryInformationFile(hFile,&StatusBlock,&FileInformation,sizeof(FILE_STANDARD_INFORMATION),FileStandardInformation);
		if(!NT_SUCCESS(Status))
		{
			RtlInitUnicodeString(&Unicode,L"NtQueryInformationFile Status:");
			*(PULONG)0 = 0;
		}
		memcpy(&FileSize,&FileInformation.EndOfFile,sizeof(LARGE_INTEGER));
		Status = NtCreateSection(&hSection,SECTION_ALL_ACCESS,0,&FileSize,PAGE_EXECUTE_READ,SEC_IMAGE,hFile);
		if(!NT_SUCCESS(Status))
		{
			RtlInitUnicodeString(&Unicode,L"NtCreateSection Status:");
			*(PULONG)0 = 0;
		}
		if(TargetRegion == 0)
		{
			Status = 0xc0000005;
			RtlInitUnicodeString(&Unicode,L"Could not find Module");
			*(PULONG)0 = 0;
		}
		else
		{
			Status = NtMapViewOfSection(hSection,TargetProcess,(PVOID*)&TargetRegion,0,0,0,&ViewSize,ViewUnmap,0,PAGE_EXECUTE_READWRITE);
			if(!NT_SUCCESS(Status))
			{
				RtlInitUnicodeString(&Unicode,L"NtMapViewOfSection Status:");
				*(PULONG)0 = 0;
			}
		}
		if(TargetProcess != INVALID_HANDLE_VALUE)
		{
			NtClose(TargetProcess);
		}
		NtClose(hFile);
		return hSection;
	}
	__except(1)
	{
		if(TargetProcess != INVALID_HANDLE_VALUE)
			NtClose(TargetProcess);
		if(hSection != INVALID_HANDLE_VALUE)
			NtClose(hSection);
		if(hFile != INVALID_HANDLE_VALUE)
			NtClose(hFile);
#ifndef _DEBUG_
		NtDisplayString(&Unicode);
		ultow(Status,(wchar_t*)&Err,16);
		RtlInitUnicodeString(&Unicode,(wchar_t*)&Err);
		NtDisplayString(&Unicode);
#else
		_ultow(Status,(wchar_t*)&Err,16);
		DbgPrint("%ws %ws",&Unicode.Buffer,&Err);
#endif
	}
	return false;
}
struct _KNOWN_EXPORT_ENTRY
{
	char *ApiName;
	ULONG EntryBaseAddress;
	ULONG EntryOffset;
	ULONG NumOfParams;
}KNOWN_EXPORT_ENTRY,*PKNOWN_EXPORT_ENTRY;
/*
PVOID Native_AddExportsToKnownTable(PVOID KnownExportsTable)
{
	ULONG i = 0;
	ULONG TotalLen = 0;
	if( KnownExportsTable != 0 )
	{
		PIMAGE_DOS_HEADER pimgDosHeader = ( PIMAGE_DOS_HEADER ) KnownExportsTable;
		PIMAGE_NT_HEADERS pimgNtHeader;
		PIMAGE_EXPORT_DIRECTORY pExport;
		if ( pimgDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
		{
			return 0;
		}
		pimgNtHeader = MakePtr( PIMAGE_NT_HEADERS, pimgDosHeader, pimgDosHeader->e_lfanew );
		if ( pimgNtHeader->Signature != IMAGE_NT_SIGNATURE )
		{
			return 0;
		}
		pExport = MakePtr( PIMAGE_EXPORT_DIRECTORY, KnownExportsTable, pimgNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress );
		unsigned short wOrdinal = 0;
		ULONG *dwNames = (ULONG *)( (ULONG)KnownExportsTable + pExport->AddressOfNames );
		ApiStruct = RtlAllocateHeap(MessageDataBuffer,0,0x8192);
		for ( i = 0; i < pExport->NumberOfNames; i++ )
		{
			strcpy((char*)((ULONG)ApiStruct + TotalLen),(const char *) KnownExportsTable + dwNames[i]))
			TotalLen += strlen((const char *) KnownExportsTable + dwNames[i])+ 1;
		}
		return (PVOID)((ULONG)ApiStruct - TotalLen);
	}
	return 0;
}
*/
FARPROC Native_GetExportAddr( PVOID hModule, const char *szFunction )
{
	if( hModule != 0 )
	{
		PIMAGE_DOS_HEADER pimgDosHeader = ( PIMAGE_DOS_HEADER ) hModule;
		PIMAGE_NT_HEADERS pimgNtHeader;
		PIMAGE_EXPORT_DIRECTORY pExport;
		if ( pimgDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
		{
			return 0;
		}
		pimgNtHeader = MakePtr( PIMAGE_NT_HEADERS, pimgDosHeader, pimgDosHeader->e_lfanew );
		if ( pimgNtHeader->Signature != IMAGE_NT_SIGNATURE )
		{
			return 0;			
		}
		pExport = MakePtr( PIMAGE_EXPORT_DIRECTORY, hModule, pimgNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress );
		unsigned short wOrdinal = 0;
		if(HIWORD( szFunction ) == 0 )
		{
			wOrdinal = (unsigned short)( LOWORD( szFunction ) - pExport->Base );

		}
		else 
		{
			ULONG *dwNames = (ULONG *)( (ULONG)hModule + pExport->AddressOfNames );
			ULONG i = 0;
			for ( i = 0; i < pExport->NumberOfNames; i++ )
			{
				if ( !strcmp( (const char *) hModule + dwNames[i], szFunction ))
				{
					wOrdinal = ( (unsigned short *) ( (ULONG)hModule + pExport->AddressOfNameOrdinals ))[i];
					break;
				}
			}
			if ( i == pExport->NumberOfNames )
			{
				return 0;
			}
		}
		if ( wOrdinal < pExport->NumberOfFunctions )
		{
			ULONG dwAddr = ( (ULONG *) ( (ULONG)hModule + pExport->AddressOfFunctions))[wOrdinal];
			return ( (FARPROC)( (ULONG)hModule + dwAddr ) );
		}
	}
	return 0;
}
PVOID GetPebDll(__in PWSTR ModName) 
{
	UNICODE_STRING UNIS;
	NTSTATUS Status = 0;
	PVOID ModBase = 0;
	RtlInitUnicodeString(&UNIS,ModName);
	Status = LdrGetDllHandle(0,0,&UNIS,&ModBase);
	if(NT_SUCCESS(Status) && ModBase != 0)
	{
		return ModBase;
	}
	else
	{
		Status = LdrLoadDll(0,0,&UNIS,&ModBase);
		if(!NT_SUCCESS(Status))
		{
			return ModBase;
		}
		return ModBase;
	}
}
//returns null on failure and positive integer relating to the API Sought
FARPROC Native_GetApi(
		__in LPWSTR ModName,
		__in char *apiname
		)
{		
	UNICODE_STRING uModName;
    FARPROC pfn = NULL;
	RtlInitUnicodeString(&uModName,ModName);
	pfn = Native_GetExportAddr(GetPebDll(uModName.Buffer),apiname);
	if(pfn != NULL)
	{
		return pfn;
	}
	return 0;
}
